﻿#region Header
// Copyright 2007-2011, Jerónimo Milea
// 
// This file is part of NetJsWire.
// 
//     NetJsWire is free software: you can redistribute it and/or modify
//     it under the terms of the GNU General Public License as published by
//     the Free Software Foundation, either version 3 of the License, or
//     (at your option) any later version.
// 
//     NetJsWire is distributed in the hope that it will be useful,
//     but WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//     GNU General Public License for more details.
// 
//     You should have received a copy of the GNU General Public License
//     along with NetJsWire.  If not, see <http://www.gnu.org/licenses/>.
#endregion

using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Design;
using System.Dynamic;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Windows.Forms;
using NetJsWire.Lite.Converters;
using NetJsWire.Lite.Editors;
using NetJsWire.Lite.Helpers;
using NetJsWire.Lite.Properties;
using Newtonsoft.Json;

namespace NetJsWire.Lite.Widgets.UI
{
    [JsonObject( MemberSerialization = MemberSerialization.OptIn )]
    [DefaultProperty( "Handler" )]
    public class ButtonLW : BoxComponent, IDynamicMetaObjectProvider
    {
        private string _icon;
        private string _iconCls;
        private bool _inButtons = true;
        private bool _internalPainting;

        public ButtonLW()
            : this( null )
        {
        }

        public ButtonLW( IContainer container )
            : base( container )
        {
            SetStyle( ControlStyles.UserPaint, true );
            SetStyle( ControlStyles.AllPaintingInWmPaint, true );
            base.Xtype = "button";
            Size = new Size( 75, 22 );
        }

        [Browsable( true )]
        [RefreshProperties( RefreshProperties.Repaint )]
        [JsonProperty( "iconCls", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [DefaultValue( ( string ) null )]
        public string IconCls
        {
            get { return _iconCls; }
            set
            {
                _iconCls = value;
                if ( !string.IsNullOrEmpty( _iconCls ) )
                    Icon = null;
            }
        }

        [Browsable( true )]
        [RefreshProperties( RefreshProperties.Repaint )]
        [JsonProperty( "icon", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [DefaultValue( ( string ) null )]
        public string Icon
        {
            get { return _icon; }
            set
            {
                _icon = value;
                if ( !string.IsNullOrEmpty( _icon ) )
                    IconCls = null;
            }
        }

        [Browsable( true )]
        [RefreshProperties( RefreshProperties.Repaint )]
        [JsonProperty( @"text" )]
        public override string Text
        {
            get
            {
                return base.Text;
            }
            set
            {
                base.Text = value;

                if ( Parent != null )
                {
                    if ( Parent is ToolbarControl )
                        Measure();

                    if ( Parent is Panel && InButtons )
                        ( ( Panel ) Parent ).InvalidateWindow();
                }

                Invalidate();
            }
        }

        [DefaultValue( 75 )]
        [JsonProperty( "width" )]
        public override int Width
        {
            get
            {
                return Size.Width;
            }
        }

        [DefaultValue( 22 )]
        [JsonProperty( "height" )]
        public override int Height
        {
            get
            {
                return Size.Height;
            }
        }

        [JsonProperty( "x" )]
        [DefaultValue( 0 )]
        [Browsable( false )]
        public override int? X
        {
            get
            {
                return base.X != ( int? ) null ? !InButtons ? base.X : null : base.X;
            }
        }

        [JsonProperty( "y" )]
        [DefaultValue( 0 )]
        [Browsable( false )]
        public override int? Y
        {
            get
            {
                return base.Y != ( int? ) null ? !InButtons ? base.Y : null : base.Y;
            }
        }

        [DefaultValue( true )]
        [Browsable( true )]
        [RefreshProperties( RefreshProperties.Repaint )]
        public bool InButtons
        {
            get
            {
                if ( Parent is ToolbarControl )
                {
                    _inButtons = false;
                }
                return _inButtons;
            }
            set
            {
                if ( !( Parent is ToolbarControl ) )
                    _inButtons = value;
                else
                    _inButtons = false;

                if ( Parent != null && Parent is Container )
                {
                    ( Parent as Container ).InvalidateWindow();
                }
            }
        }

        [Browsable( true )]
        [JsonProperty( "handler" )]
        [JsonConverter( typeof( NotQuotedJsonStringConverter ) )]
        [Editor( typeof( ScriptEditor ), typeof( UITypeEditor ) )]
        [DefaultValue( ( string ) null )]
        [Description( "A function called when the button is clicked (can be used instead of click event). The handler is passed the following parameters:\nb : Button - This Button.\ne : EventObject - The click event." )]
        public string Handler { get; set; }

        [DefaultValue( false )]
        [JsonProperty( "formBind", DefaultValueHandling = DefaultValueHandling.Ignore )]
        public bool FormBind { get; set; }

        [JsonProperty( "xtype", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Include )]
        [DefaultValue( "button" )]
        public override string Xtype
        {
            get
            {
                return base.Xtype;
            }
            set
            {
                base.Xtype = value;
            }
        }

        /// <summary>
        ///   Read-only. True if this button is pressed (only if enableToggle = true)
        /// </summary>
        [JsonProperty( "pressed" )]
        [DefaultValue( false )]
        public bool Pressed { get; set; }

        /// <summary>
        ///   Set a DOM tabIndex for this button (defaults to undefined)
        /// </summary>
        [JsonProperty( "tabIndex" )]
        [DefaultValue( 0 )]
        public new int TabIndex { get; set; }

        /// <summary>
        ///   False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true.
        /// </summary>
        [JsonProperty( "allowDepress" )]
        [DefaultValue( false )]
        public bool AllowDepress { get; set; }

        /// <summary>
        ///   True to enable pressed/not pressed toggling (defaults to false)
        /// </summary>
        [JsonProperty( "enableToggle" )]
        [DefaultValue( false )]
        public bool EnableToggle { get; set; }

        /// <summary>
        ///   Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed:<ul class = "mdetail-params">
        ///                                                                                                               <li><b>button</b> : Ext.Button<div class = "sub-desc">this Button object</div></li>
        ///                                                                                                               <li><b>state</b> : Boolean<div class = "sub-desc">The next state of the Button, true means pressed.</div></li>
        ///                                                                                                             </ul>
        /// </summary>
        [JsonProperty( "toggleHandler" )]
        [JsonConverter( typeof( NotQuotedJsonStringConverter ) )]
        [Editor( typeof( ScriptEditor ), typeof( UITypeEditor ) )]
        [DefaultValue( ( string ) null )]
        public string ToggleHandler { get; set; }

        /// <summary>
        ///   Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
        /// </summary>
        [JsonProperty( "menu", DefaultValueHandling = DefaultValueHandling.Ignore,
            NullValueHandling = NullValueHandling.Ignore )]
        [DefaultValue( null )]
        public Menu.Menu Menu { get; set; }

        /// <summary>
        ///   The position to align the menu to (see {@link Ext.Element#alignTo} for more details, defaults to 'tl-bl?').
        /// </summary>
        [JsonProperty( "menuAlign" )]
        [DefaultValue( ( string ) null )]
        public string MenuAlign { get; set; }

        /// <summary>
        ///   If used in a {@link Ext.Toolbar Toolbar}, thetext to be used if this item is shown in the overflow menu. See also
        ///   {@link Ext.Toolbar.Item}.<code>{@link Ext.Toolbar.Item#overflowText overflowText}</code>.
        /// </summary>
        [JsonProperty( "overflowText" )]
        [DefaultValue( ( string ) null )]
        public string OverflowText { get; set; }

        /// <summary>
        ///   submit, reset or button - defaults to 'button'
        /// </summary>
        [JsonProperty( "type" )]
        [DefaultValue( ( string ) null )]
        public string Type { get; set; }

        /// <summary>
        ///   The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu).
        ///   Defaults to <tt>'click'</tt>.
        /// </summary>
        [JsonProperty( "clickEvent" )]
        [DefaultValue( ( string ) null )]
        public string ClickEvent { get; set; }

        /// <summary>
        ///   False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
        /// </summary>
        [JsonProperty( "handleMouseEvents" )]
        [DefaultValue( false )]
        public bool HandleMouseEvents { get; set; }

        /// <summary>
        ///   The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.
        /// </summary>
        [JsonProperty( "tooltipType" )]
        [DefaultValue( ( string ) null )]
        public string TooltipType { get; set; }

        /// <summary>
        ///   The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.
        /// </summary>
        [JsonProperty( "tooltip", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore )]
        [JsonConverter( typeof( NotQuotedJsonStringConverter ) )]
        [DefaultValue( ( string ) null )]
        [Description( "The tooltip for the button - can be a string to be used as innerHTML (html tags are accepted) or QuickTips config object" )]
        [TypeConverter( typeof( StringConverter ) )]
        [Editor( typeof( MultilineStringEditor ), typeof( UITypeEditor ) )]
        public object Tooltip { get; set; }

        /// <summary>
        ///   <p>(Optional) A {@link Ext.DomQuery DomQuery} selector which is used to extract the active, clickable element from the
        ///     DOM structure created.</p>
        ///   <p>When a custom {@link #template} is used, you  must ensure that this selector results in the selection of
        ///     a focussable element.</p>
        ///   <p>Defaults to <b><tt>'button:first-child'</tt></b>.</p>
        /// </summary>
        [JsonProperty( "buttonSelector" )]
        [DefaultValue( ( string ) null )]
        public string ButtonSelector { get; set; }

        /// <summary>
        ///   <p>(Optional) The size of the Button. Three values are allowed:</p>
        ///   <ul class = "mdetail-params">
        ///     <li>'small'<div class = "sub-desc">Results in the button element being 16px high.</div></li>
        ///     <li>'medium'<div class = "sub-desc">Results in the button element being 24px high.</div></li>
        ///     <li>'large'<div class = "sub-desc">Results in the button element being 32px high.</div></li>
        ///   </ul>
        ///   <p>Defaults to <b><tt>'small'</tt></b>.</p>
        /// </summary>
        [JsonProperty( "scale" )]
        [DefaultValue( ( string ) null )]
        public new string Scale { get; set; }

        /// <summary>
        ///   The scope (<tt><b>this</b></tt> reference) in which the<code>{@link #handler}</code> and <code>{@link #toggleHandler}</code> is
        ///   executed. Defaults to this Button.
        /// </summary>
        [JsonProperty( "scope" )]
        [JsonConverter( typeof( NotQuotedJsonStringConverter ) )]
        [DefaultValue( ( string ) null )]
        public string Scope { get; set; }

        /// <summary>
        ///   <p>(Optional) The side of the Button box to render the icon. Four values are allowed:</p>
        ///   <ul class = "mdetail-params">
        ///     <li>'top'<div class = "sub-desc"></div></li>
        ///     <li>'right'<div class = "sub-desc"></div></li>
        ///     <li>'bottom'<div class = "sub-desc"></div></li>
        ///     <li>'left'<div class = "sub-desc"></div></li>
        ///   </ul>
        ///   <p>Defaults to <b><tt>'left'</tt></b>.</p>
        /// </summary>
        [JsonProperty( "iconAlign" )]
        [DefaultValue( ( string ) null )]
        public string IconAlign { get; set; }

        /// <summary>
        ///   <p>(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}.
        ///     Two values are allowed:</p>
        ///   <ul class = "mdetail-params">
        ///     <li>'right'<div class = "sub-desc"></div></li>
        ///     <li>'bottom'<div class = "sub-desc"></div></li>
        ///   </ul>
        ///   <p>Defaults to <b><tt>'right'</tt></b>.</p>
        /// </summary>
        [JsonProperty( "arrowAlign" )]
        [DefaultValue( ( string ) null )]
        public string ArrowAlign { get; set; }

        /// <summary>
        ///   (Optional)<p>A {@link Ext.Template Template} used to create the Button's DOM structure.</p>
        ///   Instances, or subclasses which need a different DOM structure may provide a different
        ///   template layout in conjunction with an implementation of {@link #getTemplateArgs}.
        /// </summary>
        [JsonProperty( "template" )]
        [JsonConverter( typeof( NotQuotedJsonStringConverter ) )]
        [DefaultValue( ( string ) null )]
        [Editor( typeof( ScriptEditor ), typeof( UITypeEditor ) )]
        public string Template { get; set; }

        /// <summary>
        ///   A CSS class string to apply to the button's main element.
        /// </summary>
        [JsonProperty( "cls" )]
        [DefaultValue( ( string ) null )]
        public string Cls { get; set; }

        /// <summary>
        ///   By default, if a width is not specified the button will attempt to stretch horizontally to fit its content.
        ///   If the button is being managed by a width sizing layout (hbox, fit, anchor), set this to false to prevent
        ///   the button from doing this automatic sizing.
        ///   Defaults to <tt>undefined</tt>.
        /// </summary>
        [JsonProperty( "autoWidth" )]
        [DefaultValue( false )]
        public bool AutoWidth { get; set; }

        private void Measure()
        {
            var tam = Size.Width;

            if ( Parent is ToolbarControl && !string.IsNullOrEmpty( base.Text ) )
            {
                using ( var font = new Font( "Arial", 11, FontStyle.Regular, GraphicsUnit.Pixel ) )
                {
                    var g = CreateGraphics();
                    tam = ( int ) g.MeasureString( base.Text, font ).Width;
                }

                tam += 17;
            }

            tam += ( Menu != null && Menu.ShouldSerializeItems() ) ? 6 : 0;

            if ( tam != Size.Width )
                Size = new Size( tam, Size.Height );

            if ( Parent != null && Parent is Container )
                ( ( Container ) Parent ).InvalidateWindow();

            Invalidate();
        }
        [OnSerializing]
        internal new void OnSerializingMethod( StreamingContext context )
        {
            base.OnSerializingMethod( context );
        }
        internal override void OnPaintInternal( PaintEventArgs e )
        {
            _internalPainting = InButtons && Parent is Panel;
            OnPaint( e );
            _internalPainting = false;
        }

        protected override void OnPaint( PaintEventArgs e )
        {
            //base.OnPaint( e );
            if ( !_internalPainting && InButtons && Parent is Panel )
                return;
            var outerBorder = Parent is ToolbarControl ? Color.FromArgb( 205, 220, 238 ) : Color.FromArgb( 168, 168, 168 );
            var topHalf = Parent is ToolbarControl ? Color.FromArgb( 205, 220, 238 ) : Color.FromArgb( 242, 242, 242 );
            var bottomHalf = Parent is ToolbarControl ? Color.FromArgb( 205, 220, 238 ) : Color.FromArgb( 220, 220, 220 );
            var rect = e.ClipRectangle;


            if ( !( Parent is ToolbarControl ) )
            {

                rect.Inflate( -2, -2 );
                using ( var br = new SolidBrush( topHalf ) )
                {
                    e.Graphics.FillRectangle( br, rect.X, rect.Y, rect.Width, ( rect.Height / 2 ) + 3 );

                }
                using ( var br = new SolidBrush( bottomHalf ) )
                {
                    e.Graphics.FillRectangle( br, rect.X, 3 + rect.Y + ( rect.Height / 2 ), rect.Width, rect.Height / 2 );
                }

                rect.Inflate( 2, 2 );
                using ( var outerBorderPen = new Pen( outerBorder ) )
                {
                    using ( var whitePen = new Pen( Color.White ) )
                    {
                        var roundRect = RoundedRectangle.Create( e.ClipRectangle.X, e.ClipRectangle.Y,
                                                                 e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1,
                                                                 2.5f );
                        e.Graphics.DrawPath( outerBorderPen, roundRect );
                        roundRect = RoundedRectangle.Create( e.ClipRectangle.X + 1, e.ClipRectangle.Y + 1,
                                                             e.ClipRectangle.Width - 3, e.ClipRectangle.Height - 3, 2.5f );
                        e.Graphics.DrawPath( whitePen, roundRect );
                    }
                }
            }
            using ( var font = new Font( "Arial", 11, FontStyle.Regular, GraphicsUnit.Pixel ) )
            {
                using ( var br = new SolidBrush( Disabled ? Color.Gray : Color.Black ) )
                {
                    var textSize = e.Graphics.MeasureString( Text, font );
                    TextRenderer.DrawText( e.Graphics, Text, font,
                       e.ClipRectangle,
                        Disabled ? Color.Gray : Color.Black, Color.Transparent,
                       TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis );

                    //e.Graphics.DrawString( Text, font, br, rect.X + ( Size.Width / 2 ) - textSize.Width / 2, rect.Y + ( Size.Height / 2 ) - textSize.Height / 2 );

                    if ( Parent is ToolbarControl && Menu != null && Menu.ShouldSerializeItems() )
                    {
                        e.Graphics.DrawImage( Resources.btn_arrow, rect.X + ( ( Size.Width / 2 ) - textSize.Width / 2 ) + textSize.Width + 4, ( rect.Y + ( Size.Height / 2 ) - textSize.Height / 2 ) + 5, new RectangleF( 3, 6, 7, 4 ), GraphicsUnit.Pixel );
                    }
                }
            }
        }

        protected bool ShouldSerializeSize()
        {
            return Size.Width != 75 || Size.Height != 22;
        }

        public bool ShouldSerializeXtype()
        {
            return !( Parent is ToolbarControl ) && ( !InButtons || ( InButtons && Xtype != "button" ) );
        }
    }
}
